#include <fstream>
#include <pthread.h>
#include <iostream>
#include <stdio.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <queue>
#include <string.h>

#define BUFSIZE (sizeof(long) * 8 + 1)

using namespace std;

pthread_mutex_t pc_mutex, pc_mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t consumer = PTHREAD_COND_INITIALIZER;

void spawnThreads(const int&, const int&);
void *pthreadProducer( void*);
void *pthreadConsumer( void*);
char *ltoa(long N, char *str, int base);

queue<string> theNames;

/*
*	It will take  2 numeric arguments, 
*   indicating the number of producers 
*   and the number of consumers.
*/
int main(int argn, char* args[]){

	int prods = atoi(args[1]);
	int cons = atoi(args[2]);
	
	spawnThreads(prods, cons);
	
	return 0;
}

void spawnThreads(const int& prods, const int& cons){
	
	pthread_t producers[12];
	pthread_t consumers[12];
	int largestNum;
	cerr << "I'm spawning threads" << endl;

	for (long j = 0; j < cons; j++){
		pthread_create( &consumers[j], NULL, pthreadConsumer, (void*) j); //, (void*) data );
		cerr << "made consumer " << j << endl;
	}	
	for (long i = 0; i < prods; i++){
		pthread_create( &producers[i], NULL, pthreadProducer, (void*) i);//, (void*) data );
		cerr << "made producer" << i << endl;
	}

	
	
	
	// cerr << "I've made my threads" << endl;
	// wait for them all to terminate
	if (prods > cons)
		largestNum = prods;
	else
		largestNum = cons;
	for ( int k = 0; k < largestNum; k++){
		if( k < prods)
			pthread_join( producers[k], NULL);
		// if( k < cons)
		// 	pthread_join( consumers[k], NULL);
	}
}

/*
* 	Each producer will read from a file named producerN, 
*	where N is the index of a given producer.
*	The producer will read it's file, append a "pN" where N 
*	is the index of the producer to the string, then pass the string to next
*	available consumer.
*
*/


char *ltoa(long N, char *str, int base)
{
      register int i = 2;
      long uarg;
      char *tail, *head = str, buf[BUFSIZE];

      if (36 < base || 2 > base)
            base = 10;                    /* can only use 0-9, A-Z        */
      tail = &buf[BUFSIZE - 1];           /* last character position      */
      *tail-- = '\0';

      if (10 == base && N < 0L)
      {
            *head++ = '-';
            uarg    = -N;
      }
      else  uarg = N;

      if (uarg)
      {
            for (i = 1; uarg; ++i)
            {
                  register ldiv_t r;

                  r       = ldiv(uarg, base);
                  *tail-- = (char)(r.rem + ((9L < r.rem) ?
                                  ('A' - 10L) : '0'));
                  uarg    = r.quot;
            }
      }
      else  *tail-- = '0';

      memcpy(head, ++tail, i);
      return str;
}

void* pthreadProducer( void* stuff){
	long myID = (long) stuff;
	string theName, theStuff;
	theName = "producer";
	char buffer [sizeof(long)*8+1];
	ltoa(myID, buffer, 10);
	// cerr  << "I am actually doing something" << endl;
	theName.append(buffer);
 	fstream theFile;
	theFile.open(theName.data());
	if(!theFile.is_open()){
		cerr << "Error opening input file: " << theName << endl;
	}
	else{
		while (theFile.good()) 
		{

			theFile >> theStuff;
			theStuff.append("P");
			theStuff.append(buffer);
			// cout << "The Stuff: " << theStuff << endl;
			while(pthread_mutex_trylock( &pc_mutex)){};
				theNames.push(theStuff);
			pthread_mutex_unlock( &pc_mutex);
			pthread_cond_signal( &consumer);

		}

		theFile.close();
	}
	
	while(!theNames.empty()){pthread_cond_signal( &consumer);}

	pthread_exit( NULL);
}

/*
*	Each consumer will then append a cN, when N is 
*	the index of the consumer of the string, and 
*	print out the string.
*/

void* pthreadConsumer( void* stuff){
	long myID = (long) stuff;
	string theName;
	theName = "empty";
	while(true){
		 pthread_cond_wait( &consumer, &pc_mutex2);
		// we got signaled.  try to obtain a lock on the mutex
			// we have our lock, check to make sure that something
			// is actually in the queue
			if(theNames.size() > 0){
				theName = theNames.front();
				theNames.pop();
				break;
			}

		
		// compare to "empty" 
		//  if false the string has a name and we break
		if( theName.compare("empty") == 0){
			pthread_mutex_unlock( &pc_mutex2);
		}else{
			break;
		}
		
	}
	pthread_mutex_unlock( &pc_mutex2);
	/*
		Do our thing here
	*/
	
	theName.append("c");
	char buffer [sizeof(long)*8+1];
	ltoa(myID, buffer, 10);
	theName.append(buffer);
	cout << theName << endl;
	
	// do this some more
	pthreadConsumer( (void*) myID);
}


